package com.yc.boot.sys.business;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yc.boot.common.model.response.Outcome;
import com.yc.boot.sys.entity.SysDepartment;
import com.yc.boot.sys.entity.SysDeptScope;
import com.yc.boot.sys.entity.SysUserDepartment;
import com.yc.boot.sys.service.SysDepartmentService;
import com.yc.boot.sys.service.SysDeptScopeService;
import com.yc.boot.sys.service.SysUserDepartmentService;
import com.yc.boot.sys.model.UserInfo;
import com.yc.boot.sys.model.find.FindDeptPage;
import com.yc.boot.sys.model.find.FindDeptTree;
import com.yc.boot.sys.model.save.SaveDept;
import com.yc.boot.sys.model.update.ResetUserDept;
import com.yc.boot.sys.model.update.UpdateDept;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.yc.boot.common.constant.BaseConstant.*;


/**
 * @author 杨智杰
 * @since 2021/8/23 14:37
 */
@Service
public class SysDepartmentBusiness {

    @Resource
    private SysUserBusiness sysUserBusiness;

    @Resource
    private SysDeptScopeService sysDeptScopeService;

    @Resource
    private SysDepartmentService sysDepartmentService;

    @Resource
    private SysUserDepartmentService sysUserDepartmentService;

    /**
     * 分页
     *
     * @param param 分页条件
     * @return 分页数据
     */
    public IPage<SysDepartment> page(FindDeptPage param) {
        return sysDepartmentService.page(new Page<>(param.getCurrent(), param.getSize()),
                new QueryWrapper<SysDepartment>().lambda()
                        .eq(ObjectUtil.isNotNull(param.getId()), SysDepartment::getId, param.getId())
                        .eq(ObjectUtil.isNotNull(param.getPid()), SysDepartment::getPid, param.getPid())
                        .eq(ObjectUtil.isNotNull(param.getLevel()), SysDepartment::getLevel, param.getLevel())
                        .like(StrUtil.isNotBlank(param.getName()), SysDepartment::getName, param.getName())
                        .orderByDesc(SysDepartment::getCreateTime));
    }

    /**
     * 新增
     *
     * @param param 部门信息
     * @return 是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    public Outcome save(SaveDept param) {
        UserInfo loginUser = sysUserBusiness.getUserInfo();
        SysDepartment department = Convert.convert(SysDepartment.class, param);
        department.setCreateBy(loginUser.getId());
        if (sysDepartmentService.count(new QueryWrapper<SysDepartment>().lambda()
                .eq(SysDepartment::getName, param.getName())) > ZERO) {
            return Outcome.failure("部门名重复!");
        }
        boolean saveDept = sysDepartmentService.save(department);
        if (CollUtil.isNotEmpty(param.getDataScopeList()) && saveDept) {
            boolean saveBatch = sysDeptScopeService.saveBatch(param.getDataScopeList().stream().map(id -> new SysDeptScope()
                    .setDeptId(department.getId()).setScopeId(id)).collect(Collectors.toList()));
            return Outcome.status(saveDept && saveBatch);
        }
        return Outcome.status(saveDept);
    }

    /**
     * 修改
     *
     * @param param 修改信息
     * @return 是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    public Outcome update(UpdateDept param) {
        UserInfo loginUser = sysUserBusiness.getUserInfo();
        SysDepartment department = Convert.convert(SysDepartment.class, param);
        department.setUpdateBy(loginUser.getId());
        if (StrUtil.isNotBlank(param.getName()) &&
                sysDepartmentService.count(new QueryWrapper<SysDepartment>().lambda()
                        .eq(SysDepartment::getName, param.getName())
                        .ne(SysDepartment::getId, param.getId())) > ZERO) {
            return Outcome.failure("部门名重复!");
        }
        boolean updateDept = sysDepartmentService.updateById(department);
        if (CollUtil.isNotEmpty(param.getDataScopeList()) && updateDept) {
            boolean remove = sysDeptScopeService.remove(new QueryWrapper<SysDeptScope>().lambda().eq(SysDeptScope::getDeptId, param.getId()));
            boolean saveBatch = sysDeptScopeService.saveBatch(param.getDataScopeList().stream().map(id -> new SysDeptScope()
                    .setDeptId(department.getId()).setScopeId(id)).collect(Collectors.toList()));
            return Outcome.status(updateDept && saveBatch && remove);
        }
        return Outcome.status(updateDept);
    }

    /**
     * 删除
     *
     * @param id 根据Id删除
     * @return 是否成功
     */
// (Convert.toList(SysDepartment.class, id.stream().map(i -> new SysDepartment().setUpdateBy(userInfo.getId()).setId(i))
// .collect(Collectors.toList())));
//   .collect后面接的是函数 先把集合里面的对象提取成单个再调用这个函数（这里的函数是Collectors.toList()把单个对象执行了Map里面的设值取值操作
//   后又重新打包成了一个集合） .map是把提取出来的单个对象比如这里的i就是指单个对象，进行获取值设值操作。
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(List<Long> id) {
        UserInfo userInfo = sysUserBusiness.getUserInfo();
        boolean updateBatchById = sysDepartmentService.updateBatchById(Convert.toList(SysDepartment.class, id.stream()
                .map(i -> new SysDepartment().setUpdateBy(userInfo.getId()).setId(i)).collect(Collectors.toList())));
        boolean removeByIds = sysDepartmentService.removeByIds(id);
        sysDeptScopeService.remove(new QueryWrapper<SysDeptScope>().lambda().in(SysDeptScope::getDeptId, id));
        return updateBatchById && removeByIds;
    }


    public List<Tree<String>> getDeptTree(FindDeptTree param) {
        if (ObjectUtil.isNotNull(param.getPid())) {
            return listToTree(sysDepartmentService.list(new QueryWrapper<SysDepartment>().lambda()
                    .like(StrUtil.isNotBlank(param.getDeptName()), SysDepartment::getName, param.getDeptName())
                    .last(" AND FIND_IN_SET(id, get_child_list(" + param.getPid() + "));")));
        }
        if (ObjectUtil.isNotNull(param.getCid())) {
            return listToTree(sysDepartmentService.list(new QueryWrapper<SysDepartment>().lambda()
                    .like(StrUtil.isNotBlank(param.getDeptName()), SysDepartment::getName, param.getDeptName())
                    .last(" AND FIND_IN_SET(id, get_parent_list(" + param.getCid() + "));")));
        }
        if (param.getIsAll()) {
            return listToTree(sysDepartmentService.list(new QueryWrapper<SysDepartment>().lambda()
                    .like(StrUtil.isNotBlank(param.getDeptName()), SysDepartment::getName, param.getDeptName())));
        }
        if (param.getIsLogin()) {
            return listToTree(sysUserBusiness.getUserInfo().getDeptList());
        }
        return null;
    }

    /**
     * 获取登录用户可以使用的数据范围
     *
     * @return
     */
    public Set<Long> getDataScope() {
        return getDataScope(sysUserBusiness.getUserInfo());
    }

    /**
     * 重置用户部门关联关系
     *
     * @param param
     * @return
     */
    public Outcome reset(ResetUserDept param) {
        boolean remove = sysUserDepartmentService.remove(new QueryWrapper<SysUserDepartment>().lambda().eq(SysUserDepartment::getUserId, param.getUserId()));
        boolean save = sysUserDepartmentService.saveBatch(param.getDeptIds().stream().map(id -> new SysUserDepartment()
                .setUserId(param.getUserId()).setDeptId(id)).collect(Collectors.toList()));
        return Outcome.status(remove && save);
    }

    /**
     * 获取该用户可以使用的数据范围
     *
     * @param user
     * @return
     */
    public Set<Long> getDataScope(UserInfo user) {
        Set<Long> ids = new LinkedHashSet<>();
        if (ObjectUtil.isNull(user) || CollUtil.isEmpty(user.getDeptList())) {
            return ids;
        }
        List<SysDepartment> deptList = user.getDeptList();
        if (deptList.stream().anyMatch(dept -> dept.getLevel() == FIVE)) {
            return new LinkedHashSet<>();
        }
        if (deptList.stream().anyMatch(dept -> dept.getLevel() == FOUR)) {
            List<Long> collect = deptList.stream().filter(dept -> dept.getLevel() == FOUR)
                    .map(SysDepartment::getId).collect(Collectors.toList());
            ids.addAll(collect);
            List<Long> longs = sysDeptScopeService.list(new QueryWrapper<SysDeptScope>().lambda()
                            .in(SysDeptScope::getDeptId, collect))
                    .stream().map(SysDeptScope::getScopeId).collect(Collectors.toList());
            for (Long aLong : longs) {
                sysDepartmentService.list(new QueryWrapper<SysDepartment>().lambda()
                                .last(" AND FIND_IN_SET(id, get_child_list(" + aLong + "));"))
                        .stream().map(SysDepartment::getId).forEach(l -> ids.add(l));
            }
        }
        if (deptList.stream().anyMatch(dept -> dept.getLevel() == THREE)) {
            List<Long> collect = deptList.stream().filter(dept -> dept.getLevel() == THREE)
                    .map(SysDepartment::getId).collect(Collectors.toList());
            ids.addAll(collect);
            sysDeptScopeService.list(new QueryWrapper<SysDeptScope>().lambda().in(SysDeptScope::getDeptId, collect))
                    .stream().map(SysDeptScope::getScopeId).forEach(l -> ids.add(l));
            for (Long aLong : collect) {
                sysDepartmentService.list(new QueryWrapper<SysDepartment>().lambda()
                                .last(" AND FIND_IN_SET(id, get_child_list(" + aLong + "));"))
                        .stream().map(SysDepartment::getId).forEach(l -> ids.add(l));
            }
        }
        if (deptList.stream().anyMatch(dept -> dept.getLevel() == TWO)) {
            for (Long aLong : deptList.stream().filter(dept -> dept.getLevel() == TWO)
                    .map(SysDepartment::getId).collect(Collectors.toList())) {
                sysDepartmentService.list(new QueryWrapper<SysDepartment>().lambda()
                                .last(" AND FIND_IN_SET(id, get_child_list(" + aLong + "));"))
                        .stream().map(SysDepartment::getId).forEach(l -> ids.add(l));
            }
        }
        if (deptList.stream().anyMatch(dept -> dept.getLevel() == ONE)) {
            deptList.stream().filter(dept -> dept.getLevel() == ONE).forEach(dept -> ids.add(dept.getId()));
        }
        if (deptList.stream().anyMatch(dept -> dept.getLevel() == ZERO)) {
            ids.add(-99L);
        }
        return ids;
    }

    /**
     * linkMan
     */
    private static final String LINK_MAN = "linkMan";

    /**
     * linkPhone
     */
    private static final String LINK_PHONE = "linkPhone";

    /**
     * level
     */
    private static final String LEVEL = "level";

    /**
     * status
     */
    private static final String STATUS = "status";

    /**
     * isDefault
     */
    private static final String IS_DEFAULT = "isDefault";

    /**
     * 集合转树
     *
     * @param param 部门列表
     * @return
     */
    private List<Tree<String>> listToTree(List<SysDepartment> param) {
        if (CollUtil.isEmpty(param)) {
            return null;
        }
        List<TreeNode<String>> nodeList = CollUtil.newArrayList();
        for (SysDepartment sm : param) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put(LEVEL, sm.getLevel());
            map.put(STATUS, sm.getStatus());
            map.put(LINK_MAN, sm.getLinkMan());
            map.put(LINK_PHONE, sm.getLinkPhone());
            map.put(IS_DEFAULT, sm.getIsDefault());
            nodeList.add(new TreeNode<String>().setId(sm.getId().toString())
                    .setParentId(sm.getPid().toString()).setName(sm.getName()).setExtra(map));
        }
        return TreeUtil.build(nodeList, param.stream().min(Comparator.comparing(SysDepartment::getPid)).get().getPid().toString(),
                new TreeNodeConfig().setIdKey("id").setNameKey("name").setParentIdKey("pid").setChildrenKey("children"),
                (treeNode, tree) -> {
                    tree.setId(treeNode.getId());
                    tree.setParentId(treeNode.getParentId());
                    tree.setWeight(treeNode.getWeight());
                    tree.setName(treeNode.getName());
                    tree.putExtra(LEVEL, treeNode.getExtra().get(LEVEL));
                    tree.putExtra(STATUS, treeNode.getExtra().get(STATUS));
                    tree.putExtra(LINK_MAN, treeNode.getExtra().get(LINK_MAN));
                    tree.putExtra(LINK_PHONE, treeNode.getExtra().get(LINK_PHONE));
                    tree.putExtra(IS_DEFAULT, treeNode.getExtra().get(IS_DEFAULT));
                });
    }
}
